#include <execinfo.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <cxxabi.h>
#include <time.h>
#include "helpers.h"

static long const onebillion = 1000L * 1000L * 1000L;

using namespace openitch;

void helpers::exception(const char* reason)
{
	if (0!=reason)
	{
		fputs(reason,stderr);
		fputs("\n",stderr);
	}
	stacktrace(stderr);
}

void helpers::stacktrace() {
	stacktrace(stderr);
}
// adapted from http://panthema.net/2008/0901-stacktrace-demangled/
//
// stacktrace.h (c) 2008, Timo Bingmann from http://idlebox.net/
// published under the WTFPL v2.0
//
void helpers::stacktrace(FILE *out) {//, const char *file, int line) {
    const size_t max_depth = 100;
    size_t stack_depth;
    void *stack_addrs[max_depth];
    char **stack_strings;

    stack_depth = backtrace(stack_addrs, max_depth);
    stack_strings = backtrace_symbols(stack_addrs, stack_depth);

    fprintf(out, "Call stack");// from %s:%d:\n", file, line);
	for (size_t i = 1; i < stack_depth; i++) {
		size_t sz = 200; // just a guess, template names will go much wider
		char *function = static_cast<char*>(malloc(sz));
		char *begin = 0, *end = 0;
		// find the parentheses and address offset surrounding the mangled name
		for (char *j = stack_strings[i]; *j; ++j) {
			if (*j == '(') {
				begin = j;
			}
			else if (*j == '+') {
				end = j;
			}
		}
		if (begin && end) {
			*begin++ = '\x00';
			*end = '\x00';
			// found our mangled name, now in [begin, end)

			int status;
			char *ret = abi::__cxa_demangle(begin, function, &sz, &status);
			if (ret) {
				// return value may be a realloc() of the input
				function = ret;
			}
			else {
				// demangling failed, just pretend it's a C function with no args
				strncpy(function, begin, sz);
				strncat(function, "()", sz);
				function[sz-1] = '\x00';
			}
			fprintf(out, "    %s:%s\n", stack_strings[i], function);
		}
		else
		{
			// didn't find the mangled name, just print the whole line
			fprintf(out, "    %s\n", stack_strings[i]);
		}
		free(function);
	}
    free(stack_strings); // malloc()ed by backtrace_symbols
    fflush(out);
}

long openitch::gettime()
{
	timespec thetime;
	int status = clock_gettime(CLOCK_MONOTONIC,&thetime);
	return (long)thetime.tv_sec*onebillion +thetime.tv_nsec;
}


